5.6k words
变量与环境我们之前已经实现了相当多的功能,例如波兰表达式的处理与计算,S表达式,Q表达式,我们甚至可以吧代码本身放到列表里,现在我们需要为MyLisp添加变量的功能了 不变性到目前位置,我们所添加的变量是不可变的,只是暂时我们还没有添加这个功能罢了 当我们在计算一个表达式的时候,他的基本逻辑是删除先前的事物(表达式),返回新的事物(结果) 所以我们的变量其实只是命名值的一种方式,给值分配一个名称,然后在需要的时候获取该值的副本 为了允许命名,我们需要创建一个结构体,存储命名中的所有内容的名称和值,我们称之为环境,当我们开始创建一个新的名称-表达式关系时,同时创建一个新的环境匹配他 在MyLisp中,如果我们给变量重新分配一个名称时,在底层上其实是把原先的对象删除,然后再新建一个,再分配名称,与C语言是有很大不同的 符号语法我们现在需要更新一下符号语法,以更好的适配变量命名 我们需要他灵活一点,可能匹配任何可能有效的符号,没有限制,正则表达式如下 /[a-zA-Z0-9_+\\-*\\/\\\\=<>!&]+/ 此规则允许符号是任何普通的 C 标识符字符 a-z...
4.2k words
Q表达式添加特性的一般步骤从这以后我们可以发现,给这个编程语言添加某个特性都有一定的步骤 语法:定义新的语法规则 表示:添加新的数据类型 解析:添加新的函数,正确处理AST 语义:添加新的函数,用于求值和操作 Q表达式这里我们会实现一个新的Lisp值类型,叫做Q表达式(Quoted Expression),与S表达式一样,也是Lisp表达式的一种,但他不受Lisp求值机制的作用 也就是说,当受到函数作用时,Q表达式不会被求值 因此我们可以使用Q表达式来存储和管理其他的Lisp值类型,例如数字,符号,S表达式 在添加Q表达式之后,我们需要定义一些操作来管理,类似于之前的数学操作,这些操作定义了Q表达式的具体行为 Q表达式与S表达式非常类似,不同的是Q表达式存在大括号内,S表达式存在小括号内 原生的Lisp并不支持Q表达式,他们通常使用宏来做到禁止表达式求值的功能。我们这里定义Q表达式来达到类似的效果 1234567891011121314151617181920212223242526// 创建解析器mpc_parser_t* Number = mpc_new("...
3.2k words
S表达式Lisp列表Lisp的程序代码也是数据形式的一种,这个结构被称之为S表达式,他存在一个Lisp列表中,我们需要用这个列表结构递归的表示出数字、操作符号和其他的列表,因此我们需要扩展MLval的内容,同时会重构之前写过的函数 我们需要将求值过程分为读取存入,求值两个过程 解析表达式这里就要由波兰表达式扩展到S表达式,因此首先需要更新一下语法解析器,这里笔者在创建的时候会有一部分内存泄漏的问题,但是重新写一遍这个解析器问题就消失了,也不会报错,可能是正则表达式的部分有出问题 除此之外我们需要把operator操作重命名为symbol,以便之后添加更多的操作符、变量、函数做准备 12345678910111213141516171819// 创建解析器mpc_parser_t* Number = mpc_new("number");mpc_parser_t* Symbol = mpc_new("symbol");mpc_parser_t* Sexpr = mpc_new("sexpr");mpc_parser_t* E...
2.7k words
波兰表达式计算AST树的结构在上一篇我们已经可以读取分析波兰表达式的内部结构了,这一篇我们将会对表达式进行计算,得到计算的结果 之前有提到过抽象语法树,AST,他是用来表示用户输入的的表达式的结构的,操作数和操作符这些需要被处理的数据都存在叶子节点上,而非叶子节点上存储的是遍历和求值的信息 在这里需要看一下mpc_ast_t的内部结构 1234567typedef struct mpc_ast_t { char* tag; char* contents; mpc_state_t state; int children_num; struct mpc_ast_t** children;} mpc_ast_t; tag表示节点内容之前的标签,他表示解析这个节点用到的规则,例如expr|number|regex contents包含了节点的具体内容,例如add、(,对于非叶子节点,这个内容为空 state表示这个节点所处的状态,例如行数和列数,这里不会用到 最后两个成员就是表示树的结构了,这里为了支持多叉树,采用指针数组的方式记录 运算符与数...
2.5k words
编程语言编程语言是类似于自然语言的,虽然我们是自然而然就学会了自己的母语,但实际上自然语言是建立在递归和重复的子结构之上的 在语文的学习过程中我们会学到一些语法,例如名词、动词、谓语、连词 他的结构体现在,例如谓语可以是形容词,是动词,是副词加动词 他的递归体现在,主语可以是名词,可以是形容词加名词,甚至是一个句子,再由这个主语去构成另一个句子 这些说白了是一种有限的规则,而由这些优先的规则延申出来的就是无限的句子 为了定义我们的编程语言(MyLisp),我们首先需要正确解析用户按照语法规则写的代码,因此我们就需要编写一个语法解析器,用于判断用户的输入是否合法,并且产生解析后的内部表示 但是这一步十分繁琐且枯燥,我们使用mpc库来帮助我们完成这个工作,这个库是一个解析器组合子库,可以为任何语言编写语法解析器,他极大的简化了原本枯燥无聊的工作,仅仅编写高层的抽象语法规则即可 mpc库的项目地址,非常感谢Daniel Holden大佬!! 模拟自然语言下面我们将使用mpc的一些函数,尝试描述英语的部分语法,熟悉mpc的使用步骤,更多的函数请移步项目主页 我们只采用最基础的语法,以后如...
3.3k words
生成式AI、提示词工程和零代码应用开发人工智能概念 这里我们逐一解释这些概念 AI是计算机学科下的一个分支学科,旨在使用计算机使之能像人类一样学习和思考问题 机器学习是AI的一个子集,他的一个重要特点就是不需要人去做显示编程(不用手撕函数),让计算机自行学习迭代总结规律,做出预测和决策 机器学习机器学习下就有很多分支了,例如监督学习、无监督学习、强化学习 监督学习目标是学习原始数据和标签之间的映射关系,他的一个特点就是是给定了原始数据和标签,让计算机自行总结归纳,他的经典的应用有对事物的分类、回归预测等 无监督学习就是相对于监督学习,他只给原始数据而不给定标签,让计算机自行发现数据中的模式或规律,他的应用有聚类(就是分组)、降维、异值检测、自编码器(大语言模型中用到)、自监督学习等 强化学习就是让模型在环境中采取行动,不断尝试,让他在环境中获得最大奖励的手段,类似于人类学习的模式,趋利避害,例如Alpha Go 深度学习深度学习具有这三类学习的一些特点,也有自己的模式,属于都有点但都不是的状态 关于深度学习的核心就是两个词,一是连接,二是深度 连接的意思是说,他采用了类似...
895 words
项目介绍这是一个超轻量级的C/C++项目,使用C/C++模拟实现一个简单的Lisp(一种函数式程序设计语言),会使用一些外部的库进行初始化工作,例如mpc、editline等 我们会使用类Python的REPL模式,使用Lisp程序,并不直接提供系统的编译运行环境,整个代码量大概在1000行左右 主要难点 语法规则与解析用户输入 递归与错误处理 S表达式,Q表达式 函数,标准库 我将会在这里逐步实现这个小项目并记录实现过程中出现的问题以及解决方案 会先使用C语言实现,再考虑使用C++补充和优化 读取输入的实现基本思路首先这是一个类Python的命令行输入的模式,基本思路就是使用一个while(1)循环,然后不断读入,再进行解析处理 如下 12345while(1){ char* input = readline("MyLisp> "); printf("The sentence is %s\n", input);} 这里的readline是为了适配linux和mac,我们重新写的一个有相似...
C++
1.5k words
map和set关联式容器我们在之前讲过STL的一些基础容器,例如vector,list,deque,forward_list等 这些其实统一都称为序列式容器,因为其底层都是线性的序列数据结构,而且存储的内容是元素本身 关联式容器也是存储数据的容器,不同的是,里面存储的是<key,value>的键值对,类似于我们之前讲的二叉搜索树的KV模型 键值对键值对是我们用来表示一一对应关系的结构,包含key和value两个成员变量 key表示关键字,类似于字典中的单词,value表示具体的值,也就是字典中对应的具体释义 在C++中使用pair表示一对值,但并没有具体的对应关系,只是将这一对值进行了封装,可以读取和写入,当然pair也有一些非常有意思的内容,当我们观察其源码时可以看出 这里给出SGI-STL对pair的定义 12345678910111213141516171819template<class T1, class T2>struct pair{ typedef T1 first_type; typedef T2 second_type; ...